//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//
//--------------------------------------------------------------------------;
//  File:       ddmm.cpp
//  Content:    Routines for using DirectDraw on a multimonitor system
//--------------------------------------------------------------------------;

#include <streams.h>
#include <ddraw.h>
#include "ddmm.h"

#define COMPILE_MULTIMON_STUBS
//#include "multimon.h"

/*
 * FindDeviceCallback
 */
typedef struct {
	LPSTR   szDevice;
	GUID*   lpGUID;
	GUID    GUID;
	BOOL    fFound;
}   FindDeviceData;

BOOL CALLBACK FindDeviceCallback(GUID* lpGUID, LPSTR szName, LPSTR szDevice, LPVOID lParam)
{
	FindDeviceData *p = (FindDeviceData*)lParam;

	if (lstrcmpiA(p->szDevice, szDevice) == 0) {
	    if (lpGUID) {
		p->GUID = *lpGUID;
		p->lpGUID = &p->GUID;
	    } else {
		p->lpGUID = NULL;
	    }
	    p->fFound = TRUE;
	    return FALSE;
	}
	return TRUE;
}


BOOL CALLBACK FindDeviceCallbackEx(GUID* lpGUID, LPSTR szName, LPSTR szDevice, LPVOID lParam, HMONITOR hMonitor)
{
	FindDeviceData *p = (FindDeviceData*)lParam;

	if (lstrcmpiA(p->szDevice, szDevice) == 0) {
	    if (lpGUID) {
		p->GUID = *lpGUID;
		p->lpGUID = &p->GUID;
	    } else {
		p->lpGUID = NULL;
	    }
	    p->fFound = TRUE;
	    return FALSE;
	}
	return TRUE;
}


/*
 * DirectDrawCreateFromDevice
 *
 * create a DirectDraw object for a particular device
 */
IDirectDraw * DirectDrawCreateFromDevice(LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, PDRAWENUM DirectDrawEnumerateP)
{
	IDirectDraw*    pdd = NULL;
	FindDeviceData  find;

	if (szDevice == NULL) {
		DirectDrawCreateP(NULL, &pdd, NULL);
		return pdd;
	}

	find.szDevice = szDevice;
	find.fFound   = FALSE;
	//DirectDrawEnumerateP(FindDeviceCallback, (LPVOID)&find);

	if (find.fFound)
	{
		//
		// In 4bpp mode the following DDraw call causes a message box to be popped
		// up by DDraw (!?!).  It's DDraw's fault, but we don't like it.  So we
		// make sure it doesn't happen.
		//
		//UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
		DirectDrawCreateP(find.lpGUID, &pdd, NULL);
		//SetErrorMode(ErrorMode);
	}

	return pdd;
}


/*
 * DirectDrawCreateFromDeviceEx
 *
 * create a DirectDraw object for a particular device
 */
//IDirectDraw * DirectDrawCreateFromDeviceEx(LPSTR szDevice, PDRAWCREATE DirectDrawCreateP, LPDIRECTDRAWENUMERATEEXA DirectDrawEnumerateExP)
//{
//	IDirectDraw*    pdd = NULL;
//	FindDeviceData  find;
//
//	if (szDevice == NULL) {
//		DirectDrawCreateP(NULL, &pdd, NULL);
//		return pdd;
//	}
//
//	find.szDevice = szDevice;
//	find.fFound   = FALSE;
//	/*DirectDrawEnumerateExP(FindDeviceCallbackEx, (LPVOID)&find,
//					DDENUM_ATTACHEDSECONDARYDEVICES);*/
//
//	if (find.fFound)
//	{
//		//
//		// In 4bpp mode the following DDraw call causes a message box to be popped
//		// up by DDraw (!?!).  It's DDraw's fault, but we don't like it.  So we
//		// make sure it doesn't happen.
//		//
//		//UINT ErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
//		DirectDrawCreateP(find.lpGUID, &pdd, NULL);
//		//SetErrorMode(ErrorMode);
//	}
//
//	return pdd;
//}

/*
 *  OneMonitorCallback
 */
BOOL CALLBACK OneMonitorCallback(HMONITOR hMonitor, HDC hdc, LPRECT prc, LPARAM lParam)
{
    HMONITOR *phMonitorFound = (HMONITOR *)lParam;

    if (*phMonitorFound == 0)
        *phMonitorFound = hMonitor;
    else
        *phMonitorFound = (HMONITOR)INVALID_HANDLE_VALUE;

    return TRUE;
}

/*
 *  OneMonitorFromWindow
 *
 *  similar to the Win32 function MonitorFromWindow, except
 *  only returns a HMONITOR if a window is on a single monitor.
 *
 *  if the window handle is NULL, the primary monitor is returned
 *  if the window is not visible returns NULL
 *  if the window is on a single monitor returns its HMONITOR
 *  if the window is on more than on monitor returns INVALID_HANDLE_VALUE
 */
HMONITOR OneMonitorFromWindow(HWND hwnd)
{
    HMONITOR hMonitor = NULL;
    RECT rc;

    if (hwnd)
    {
        GetClientRect(hwnd, &rc);
        ClientToScreen(hwnd, (LPPOINT)&rc);
        ClientToScreen(hwnd, (LPPOINT)&rc+1);
    }
    else
    {
	// Todd, looky here
        SetRect(&rc,0,0,10,10);
        //SetRectEmpty(&rc);
    }

    EnumDisplayMonitors(NULL, &rc, OneMonitorCallback, (LPARAM)&hMonitor);
    return hMonitor;
}

/*
 * DeviceFromWindow
 *
 * find the direct draw device that should be used for a given window
 *
 * the return code is a "unique id" for the device, it should be used
 * to determine when your window moves from one device to another.
 *
 *      case WM_MOVE:
 *          if (MyDevice != DirectDrawDeviceFromWindow(hwnd,NULL,NULL))
 *          {
 *              // handle moving to a new device.
 *          }
 *
 */
INT_PTR DeviceFromWindow(HWND hwnd, wchar_t * szDevice, RECT *prc)
{
    HMONITOR hMonitor;

    if (GetSystemMetrics(SM_CMONITORS) <= 1)
    {
        if (prc)
			SetRect(prc,0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
        if (szDevice)
			lstrcpy(szDevice, L"DISPLAY");
        return -1;
    }

    hMonitor = OneMonitorFromWindow(hwnd);

    if (hMonitor == NULL || hMonitor == INVALID_HANDLE_VALUE)
    {
	if (prc) SetRectEmpty(prc);
	if (szDevice) *szDevice=0;
        return 0;
    }
    else
    {
	if (prc != NULL || szDevice != NULL)
	{
	    MONITORINFOEX mi;
	    mi.cbSize = sizeof(mi);
	    GetMonitorInfo(hMonitor, &mi);
	    if (prc) *prc = mi.rcMonitor;
	    if (szDevice) lstrcpy(szDevice, mi.szDevice);
	}
        return (INT_PTR)hMonitor;
    }
}
